test(e2e): W1 LIVE auth/login coverage (OAuth state-replay, logout-revocation, CLI device-flow, /auth/me, CORS)#172
Merged
Conversation
…, /auth/me, CORS)
Adds e2e/live-auth.spec.ts — the W1 (P0) real-backend auth suite from
docs/sessions/2026-06-04/USER-FLOW-INVENTORY-AND-TEST-MATRIX.md §W1. Closes
the matrix gap whose failure surface caused the 2026-05-29→30 prod-login
outage (a UI↔backend auth-integration regression).
Legs covered (E2E_LIVE=1-gated; whole file skips loudly in per-PR CI so the
gate never depends on a live backend):
- A4/A6 GitHub OAuth: start → github.com authorize w/ single-use state;
forged/never-issued state callback must fail closed (no session_token).
- A7 /auth/exchange CORS contract (ACAC:true + ACAO:https://instanode.dev) —
the AUTH-004 surface.
- A8/A9 /auth/me: valid synthetic bearer → 200 + claimed email + tier='free';
tampered + expired bearers → 401.
- A10 logout revocation (the precise incident class): valid bearer 200 →
POST /auth/logout → SAME bearer/jti on /auth/me → 401.
- A12/A13 CLI device-flow: POST /auth/cli auth_url host == canonical
instanode.dev (parsed hostname, not the api host); pre-approval poll →
status, no api_token.
- A1 magic-link START leg → 2xx {ok:true}; the FULL callback flow is
test.skip with an explicit "blocked on Brevo sender validation (operator)"
annotation, NOT a silent omission.
Mint path is Brevo-free (TEST-ACCOUNTS-AND-NR-SYNTHETICS-PLAN.md §1.1):
account legs provision→/claim a real user/team then mint an HS256 session JWT
with E2E_JWT_SECRET; they self-skip loudly when the secret is unset while the
contract-only legs still run. Every minted resource is cohort-branded +
ledgered-before-assert + reaped inline / afterAll / reap-cohort.ts (rule 24).
Mirrors live-anon-provision + auth-roundtrip patterns; named live-*.spec.ts so
playwright.live.config.ts picks it up and the default mocked config ignores it.
Wires optional E2E_JWT_SECRET into e2e-live.yml.
npm run gate: GREEN (tsc --noEmit + build + vitest 1115 passed / 3 skipped,
80 files; mocked suite unchanged). live config lists 11 tests, all skip
cleanly without E2E_LIVE.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mastermanas805
enabled auto-merge (squash)
June 4, 2026 19:34
size-limit report 📦
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements W1 (P0) from
docs/sessions/2026-06-04/USER-FLOW-INVENTORY-AND-TEST-MATRIX.md§W1 — the highest-priority auth block. The past prod-login outage (2026-05-29 → 30) was a UI↔backend auth-integration gap; this adds real-backend E2E for every leg of that surface ase2e/live-auth.spec.ts.Built on the WS1-P1 LIVE harness (
cohort.ts,cleanup-ledger.ts,reap-cohort.ts,playwright.live.config.ts,e2e-live.yml) and the W2 spec (live-anon-provision.spec.ts) — mirrors theirE2E_LIVE=1gating + ledger-before-assert + reap patterns.Legs covered (matrix IDs)
GET /auth/github/start→ 3xx togithub.comauthorize w/ a single-usestateparamstateon the callback fails closed — nosession_tokenminted, not a 200OPTIONS /auth/exchange→ACAC:true+ACAO:https://instanode.dev/auth/mevalid bearertier='free'/auth/mebad bearerPOST /auth/logout→ SAME bearer/jti → 401 (the precise incident class)POST /auth/cliauth_urlhost == canonical instanode.dev (parsed hostname, not the api host); pre-approval poll →status, noapi_tokenPOST /auth/email/start→ 2xx{ok:true}Skip-annotated (NOT silently omitted)
test.skipwith an explicit reason: blocked on Brevo sender validation (operator action — validatenoreply@instanode.dev;project_brevo_sender_not_validated.md). The emailed link can't be delivered until then, so start-leg only is exercised.Mint path (Brevo-free)
Per
TEST-ACCOUNTS-AND-NR-SYNTHETICS-PLAN.md§1.1: account legs provision an anon cache →/claiminto a real user/team → mint an HS256 session JWT withE2E_JWT_SECRET(no email round-trip, no GitHub). Those legs self-skip loudly whenE2E_JWT_SECRETis unset; the contract-only legs (OAuth state-replay, CORS, CLI canonical host, magic-link start, tampered token) still run.e2e-live.ymlnow passes optionalE2E_JWT_SECRET.Safety (rule 24)
Every minted resource is cohort-branded + ledgered before any throwing assertion + reaped inline /
afterAll/ out-of-processreap-cohort.ts. Namedlive-*.spec.tssoplaywright.live.config.tspicks it up and the default mocked per-PR config ignores it — per-PR CI never needs a live backend (all 11 tests skip cleanly withoutE2E_LIVE=1).Gate
npm run gate(tsc --noEmit && npm run build && vitest run) GREEN — 80 files, 1115 passed / 3 skipped; mocked suite unchanged.🤖 Generated with Claude Code